package com.controller;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.apache.log4j.Logger;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.springframework.web.jsf.FacesContextUtils;

import com.builder.ActualizacionTarifarioBuilder;
import com.builder.FamiliaBuilder;
import com.builder.ProductoBuilder;
import com.dao.BaseModelDAO;
import com.model.ActualizacionTarifario;
import com.model.Familia;
import com.model.Producto;
import com.model.ProductoPrecio;
import com.model.Tarifario;
import com.query.ActualizacionTarifarioQuery;
import com.query.TarifarioQuery;
import com.type.TipoActualizacionEnum;
import com.type.TipoTarifarioEnum;
import com.util.JSFUtil;
import com.view.ActualizacionTarifarioView;
import com.view.FamiliaView;
import com.view.ProductoView;

@ManagedBean
@ViewScoped
@SuppressWarnings("serial")
public class ActualizacionTarifarioController extends
		PaginableController<ActualizacionTarifario> {

	private Logger log = Logger
			.getLogger(ActualizacionTarifarioController.class);
	private BaseModelDAO<ActualizacionTarifario> dao;
	private BaseModelDAO<Familia> familiaDao;
	private BaseModelDAO<Producto> productoDao;
	private BaseModelDAO<Tarifario> tarifarioDao;
	private ActualizacionTarifario model;
	private ActualizacionTarifarioQuery query;
	private List<FamiliaView> listaFamilia;
	private List<ProductoView> listaProducto;
	private List<TipoActualizacionEnum> listaTipoActualizacion;
	private List<TipoTarifarioEnum> listaTipoTarifario;

	@ManagedProperty("#{actualizacionTarifarioView}")
	private ActualizacionTarifarioView view;

	@ManagedProperty("#{actualizacionTarifarioBuilder}")
	private ActualizacionTarifarioBuilder builder;

	@ManagedProperty("#{familiaBuilder}")
	private FamiliaBuilder familiaBuilder;

	@ManagedProperty("#{productoBuilder}")
	private ProductoBuilder productoBuilder;

	@SuppressWarnings("unchecked")
	public ActualizacionTarifarioController() {

		try {
			dao = (BaseModelDAO<ActualizacionTarifario>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("actualizacionTarifarioDAO");
			familiaDao = (BaseModelDAO<Familia>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("familiaDAO");
			productoDao = (BaseModelDAO<Producto>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("productoDAO");
			tarifarioDao = (BaseModelDAO<Tarifario>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("tarifarioDAO");

			query = new ActualizacionTarifarioQuery();

			listaTipoActualizacion = Arrays.asList(TipoActualizacionEnum
					.values());

			listaTipoTarifario = Arrays.asList(TipoTarifarioEnum.values());

			addEdit = false;

		} catch (Throwable e) {
			log.error(
					"Error al inicializar la clase ActualizacionTarifarioController",
					e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@PostConstruct
	public void init() {
		listaFamilia = familiaBuilder.toView(familiaDao.getList());
	}

	public ActualizacionTarifarioQuery getQuery() {
		return query;
	}

	public void setQuery(ActualizacionTarifarioQuery query) {
		this.query = query;
	}

	public ActualizacionTarifario getModel() {
		return model;
	}

	public void setModel(ActualizacionTarifario model) {
		this.model = model;
	}

	public ActualizacionTarifarioView getView() {
		return view;
	}

	public void setView(ActualizacionTarifarioView view) {
		this.view = view;
	}

	public ActualizacionTarifarioBuilder getBuilder() {
		return builder;
	}

	public void setBuilder(ActualizacionTarifarioBuilder builder) {
		this.builder = builder;
	}

	public List<TipoActualizacionEnum> getListaTipoActualizacion() {
		return listaTipoActualizacion;
	}

	public void setListaTipoActualizacion(
			List<TipoActualizacionEnum> listaTipoActualizacion) {
		this.listaTipoActualizacion = listaTipoActualizacion;
	}

	public FamiliaBuilder getFamiliaBuilder() {
		return familiaBuilder;
	}

	public void setFamiliaBuilder(FamiliaBuilder familiaBuilder) {
		this.familiaBuilder = familiaBuilder;
	}

	public List<FamiliaView> getListaFamilia() {
		return listaFamilia;
	}

	public List<ProductoView> getListaProducto() {
		return listaProducto;
	}

	public ProductoBuilder getProductoBuilder() {
		return productoBuilder;
	}

	public void setProductoBuilder(ProductoBuilder productoBuilder) {
		this.productoBuilder = productoBuilder;
	}

	public List<TipoTarifarioEnum> getListaTipoTarifario() {
		return listaTipoTarifario;
	}

	public void setListaTipoTarifario(List<TipoTarifarioEnum> listaTipoTarifario) {
		this.listaTipoTarifario = listaTipoTarifario;
	}

	public boolean isProductoSelected() {
		return (view.getProducto().getId() != null);
	}

	public void deselecionarProducto(ActionEvent event) {
		view.setProducto(new ProductoView());
	}

	public void productoSelectChange(SelectEvent event) {
		view.setProducto((ProductoView) event.getObject());
		System.out.println("se eligio una un producto "
				+ view.getProducto().getDetalle());
	}

	@Override
	public void query(ActionEvent event) {
		loadList();
	}

	@Override
	public void edit(ActionEvent event) {

		// No se puede editar. Se tiene que generar un movimiento inverso para
		// deshacer la operación.

	}

	@Override
	public void delete(ActionEvent event) {

		// No se puede borrar ya que la operación ya se realizó.
	}

	@Override
	public void add(ActionEvent event) {
		clear();
		addEdit = true;
	}

	@Override
	public void save(ActionEvent event) {

		try {

			model = builder.toDomain(view);

			if (model.getFamilia().getID() == null) {
				model.setFamilia(null);
			}

			if (model.getID() != null) {
				dao.edit(model);
				addEdit = false;
			} else {
				model.setFecha(new Date());
				dao.save(model);
			}

			// Traigo los tarifarios a actualizar
			TarifarioQuery tq = new TarifarioQuery(null,
					view.getTipoTarifario(), null, null, null,
					view.getTarifarioFuturos(), new Date());
			List<Tarifario> listaTarifario = tarifarioDao.getList(tq);

			// actualizar los precios.
			actualizarTarifarios(view, listaTarifario);

			clear();
			JSFUtil.saveMessage("Elemento guardado con exito",
					FacesMessage.SEVERITY_INFO);
			if (!addEdit) {
				loadList();
			}

		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void cancel(ActionEvent event) {
		addEdit = false;
		lazyDM = null;
		clear();
	}

	@Override
	public void clear() {
		view = new ActualizacionTarifarioView();
		model = new ActualizacionTarifario();
	}

	private void loadList() {

		lazyDM = new LazyDataModel<ActualizacionTarifario>() {
			@Override
			public List<ActualizacionTarifario> load(int first, int pageSize,
					String sortField, SortOrder sortOrder,
					Map<String, Object> filters) {

				Map<String, Object> filtro = new HashMap<String, Object>();
				filtro.put("fecha", query.getFecha());
				filtro.put("familia.id", query.getFamiliaID());
				filtro.put("producto.id", query.getProductoID());

				return dao.getList(first, pageSize, "fecha", false, filtro,
						true);
			}
		};

		Map<String, Object> filtro = new HashMap<String, Object>();
		filtro.put("fecha", query.getFecha());
		filtro.put("familia.id", query.getFamiliaID());
		filtro.put("producto.id", query.getProductoID());
		lazyDM.setRowCount(dao.count(filtro, true).intValue());
	}

	// public boolean isFamiliaSelected() {
	// return view.getFamilia().getId() != null;
	// }
	//
	// public void familiaListener() {
	// view.setProducto(new ProductoView());
	//
	// if (isFamiliaSelected()) {
	// // traigo los productos de la familia seleccionada
	// listaProducto = productoBuilder.toView(productoDao
	// .getList(new ProductoQuery(null, null, null, null, view
	// .getFamilia().getId())));
	// } else {
	// listaProducto = new ArrayList<ProductoView>();
	// }
	// }

	private void actualizarTarifarios(ActualizacionTarifarioView atv,
			List<Tarifario> listaTarifario) {

		for (Tarifario tarifario : listaTarifario) {

			// inicializo las listas de productoPrecios
			tarifario = tarifarioDao.findFULL(tarifario.getID());

			// si actualizo por producto
			if (atv.getProducto() != null) {
				actualizarPreciosPorProducto(atv, tarifario, atv.getProducto());
			} else if (atv.getFamilia().getId() != null) {
				// actualizo por familia
				actualizarPreciosPorFamilia(atv, tarifario, atv.getFamilia());
			} else {
				// actualizo todos los productos
				actualizarPreciosTodos(atv, tarifario);
			}

			// actualizo el tarifario en la base
			tarifarioDao.edit(tarifario);
		}
	}

	private void actualizarPreciosPorFamilia(ActualizacionTarifarioView atv,
			Tarifario tarifario, FamiliaView familia) {
		for (ProductoPrecio pp : tarifario.getListaProductoPrecio()) {
			if (pp.getProducto().getFamilia().getID().intValue() == familia
					.getId().intValue()) {
				actualizarPrecios(atv, pp);
			}
		}
	}

	private void actualizarPreciosPorProducto(ActualizacionTarifarioView atv,
			Tarifario tarifario, ProductoView producto) {
		for (ProductoPrecio pp : tarifario.getListaProductoPrecio()) {
			if (pp.getProducto().getID().intValue() == producto.getId()
					.intValue()) {
				actualizarPrecios(atv, pp);
			}
		}
	}

	private void actualizarPreciosTodos(ActualizacionTarifarioView atv,
			Tarifario tarifario) {
		for (ProductoPrecio pp : tarifario.getListaProductoPrecio()) {
			actualizarPrecios(atv, pp);
		}
	}

	private void actualizarPrecios(ActualizacionTarifarioView atv,
			ProductoPrecio pp) {
		if (atv.getTipoActualizacion().equals(
				TipoActualizacionEnum.PORCENTAJE_POSITIVO)) {
			BigDecimal valor = pp.getPrecio().multiply(atv.getValor())
					.divide(new BigDecimal(100));
			pp.setPrecio(pp.getPrecio().add(valor));
		} else if (atv.getTipoActualizacion().equals(
				TipoActualizacionEnum.PORCENTAJE_NEGATIVO)) {
			BigDecimal valor = pp.getPrecio().multiply(atv.getValor())
					.divide(new BigDecimal(100)).negate();
			pp.setPrecio(pp.getPrecio().add(valor));
		} else if (atv.getTipoActualizacion().equals(
				TipoActualizacionEnum.MONTO_POSITIVO)) {
			pp.setPrecio(pp.getPrecio().add(atv.getValor()));
		} else if (atv.getTipoActualizacion().equals(
				TipoActualizacionEnum.MONTO_NEGATIVO)) {
			pp.setPrecio(pp.getPrecio().subtract(atv.getValor()));
		}
	}

	public List<ProductoView> completeProducto(String query) {
		List<ProductoView> listaProducto = null;
		try {

			List<Producto> listaProductosEntity = productoDao.getList(query);
			if (listaProductosEntity != null && listaProductosEntity.size() > 0) {
				listaProducto = productoBuilder.toView(listaProductosEntity);
			}

		} catch (Throwable e) {
			log.error("Error en le metodo completeProducto: ", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
		return listaProducto;
	}

}
